<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>虎牙播放</title>
    <link type="text/css" rel="stylesheet" href="css/index.css"/>
    <script src="js/md5.js"></script>
    <script src="js/axios.min.js"></script>
    <script src="js/vue.js"></script>
    <script src="js/flv.min.js"></script>
    <script src="js/hls.min.js"></script>
    <script src="js/DPlayer.min.js"></script>

</head>
<body>
    <div id="app">
        <div id="play-head">
            <input type="text" v-model="searchContent" placeholder="输入链接或房间号">
            <button v-on:click="loadLiveInfo">播放</button>
        </div>

        <div id="play-main">
            <div id="player">
                <div id="leave-tip" v-if="isLive">{{isLive ? '该主播已下播' : ''}}</div>
            </div>
            <div id="video-desc">
                <div id="avatar">
                    <img id="avatar-img" :src="liveInfo.avatar"/>
                    <div>{{liveInfo.name}}</div>
                </div>
                <div id="basic-info">
                    <div class="mg-ud-3">uid：{{liveInfo.uid}}</div>
                    <div class="mg-ud-3">性别：{{liveInfo.sex}}</div>
                    <div class="mg-ud-3">房间号：{{liveInfo.roomId}}</div>
                    <div class="mg-ud-3">游戏名称：{{liveInfo.gameName}}</div>
                    <div class="mg-ud-3">开播时间：{{liveInfo.startTime}}</div>
                    <div class="mg-ud-3">直播标题：{{liveInfo.liveTitle}}</div>
                </div>
<!--                <div class="mg-ud-3">播放源：</div>-->
<!--                <div id="video-choice-info">-->
<!--                    <div class="video-item" v-for="item in flvStreamConfig" :key="item.id">-->
<!--                        {{item.cdnType}}-{{item.flvUrlSuffix}}-->
<!--                    </div>-->
<!--                    <div class="video-item" v-for="item in m3u8StreamConfig" :key="item.id">-->
<!--                        {{item.cdnType}}-hls-->
<!--                    </div>-->
<!--                </div>-->
<!--                <div class="mg-ud-3">清晰度：</div>-->
<!--                <div id="video-rate">-->
<!--                    <div class="rate-item" v-for="item in rateConfig" :key="item.id" @click="changeRate(item.rate)">-->
<!--                        {{item.name}}-->
<!--                    </div>-->
<!--                </div>-->
            </div>
        </div>

        <div id="play-log">
            <div class="log-item">播放地址：{{log.url}}</div>
            <div class="log-item">清晰率：{{log.rate}}</div>
        </div>


    </div>
<script>
  const { createApp, reactive, ref, onBeforeUnmount, onMounted } = Vue
  createApp({
    setup(){
        // init
        const baseRequestUrl = 'http://192.168.0.162:5000/live/huya';
        //
        const log = reactive({
            url: '',
            rate: ''
        });
        const searchContent = ref("")
        //
        const playUrl = ref('')
        const rateConfig = ref();
        const flvStreamConfig = ref([]);
        const liveInfo = reactive({
            uid: '',
            name: '',
            avatar: '',
            sex: '',
            roomId: '',
            gameName: '',
            startTime: '',
            liveTitle: '',
        });
        const m3u8StreamConfig = ref([]);
        const rate = ref(2000);
        const isLive = ref(false)
        //
        const regex = {
            num: new RegExp('[1-9]\\d*'),
            url: new RegExp('^((https|http|ftp|rtsp|mms)?:\\/\\/)[^\\s]+')
        }
        //
        let Base64 = {
            _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
            encode: function(e) {
                let t = "";
                let n, r, i, s, o, u, a;
                let f = 0;
                e = Base64._utf8_encode(e);
                while (f < e.length) {
                    n = e.charCodeAt(f++);
                    r = e.charCodeAt(f++);
                    i = e.charCodeAt(f++);
                    s = n >> 2;
                    o = (n & 3) << 4 | r >> 4;
                    u = (r & 15) << 2 | i >> 6;
                    a = i & 63;
                    if (isNaN(r)) {
                        u = a = 64
                    } else if (isNaN(i)) {
                        a = 64
                    }
                    t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
                }
                return t
            },
            decode: function(e) {
                let t = "";
                let n, r, i;
                let s, o, u, a;
                let f = 0;
                e = e.replace(/[^A-Za-z0-9+/=]/g, "");
                while (f < e.length) {
                    s = this._keyStr.indexOf(e.charAt(f++));
                    o = this._keyStr.indexOf(e.charAt(f++));
                    u = this._keyStr.indexOf(e.charAt(f++));
                    a = this._keyStr.indexOf(e.charAt(f++));
                    n = s << 2 | o >> 4;
                    r = (o & 15) << 4 | u >> 2;
                    i = (u & 3) << 6 | a;
                    t = t + String.fromCharCode(n);
                    if (u !== 64) {
                        t = t + String.fromCharCode(r)
                    }
                    if (a !== 64) {
                        t = t + String.fromCharCode(i)
                    }
                }
                t = Base64._utf8_decode(t);
                return t
            },
            _utf8_encode: function(e) {
                e = e.replace(/rn/g, "n");
                let t = "";
                for (let n = 0; n < e.length; n++) {
                    let r = e.charCodeAt(n);
                    if (r < 128) {
                        t += String.fromCharCode(r)
                    } else if (r > 127 && r < 2048) {
                        t += String.fromCharCode(r >> 6 | 192);
                        t += String.fromCharCode(r & 63 | 128)
                    } else {
                        t += String.fromCharCode(r >> 12 | 224);
                        t += String.fromCharCode(r >> 6 & 63 | 128);
                        t += String.fromCharCode(r & 63 | 128)
                    }
                }
                return t
            },
            _utf8_decode: function(e) {
                let t = "";
                let n = 0;
                let r = c1 = c2 = 0;
                while (n < e.length) {
                    r = e.charCodeAt(n);
                    if (r < 128) {
                        t += String.fromCharCode(r);
                        n++
                    } else if (r > 191 && r < 224) {
                        c2 = e.charCodeAt(n + 1);
                        t += String.fromCharCode((r & 31) << 6 | c2 & 63);
                        n += 2
                    } else {
                        c2 = e.charCodeAt(n + 1);
                        c3 = e.charCodeAt(n + 2);
                        t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
                        n += 3
                    }
                }
                return t
            }
        }

        // dp
        let dp = null;
        // 创建
        const createFlvVideo = (url)=>{
            if(dp !== null){destroyVideo();}
            //
            dp = new DPlayer({
            container: document.getElementById('player'),
            video: {
                url: url,
                type: 'customFlv',
                customType: {
                    customFlv: function (video, player) {
                        const flvPlayer = flvjs.createPlayer({
                            type: 'flv',
                            url: video.src,
                        });
                        flvPlayer.attachMediaElement(video);
                        flvPlayer.load();
                    },
                },
            },
            hotkey: true,
            preload: 'auto',
        });

        }
        // 销毁
        const destroyVideo = ()=>{
          if(dp === null){return;}
          dp.destroy();
        }

        // methods
        // 请求直播信息
        const loadLiveInfo = ()=>{
            const numAct = regex.num.test(searchContent.value)
            const urlAct = regex.url.test(searchContent.value)
            let requestUrl = '';
            if (numAct) {
                requestUrl = 'https://www.huya.com/' + searchContent.value;
            }
            else if(urlAct){
                requestUrl = searchContent.value;
            }else{
                return;
            }
            axios.get(baseRequestUrl+"?url="+requestUrl
            ).then(response =>{
                if(response.data.code !== 200){
                    throw "response error";
                }
                const data = response.data.data;
                createLiveRoomInfo(data['liveInfo']);
                rateConfig.value = data['configInfo'];
                flvStreamConfig.value = data['flvStreamInfo'];
                m3u8StreamConfig.value = data['m3u8StreamInfo'];
                // 构建并播放
                const length = data['flvStreamInfo']
                if(length < 1){
                    isLive.value = true;
                    return;
                }
                rate.value = data['configInfo'][0]['rate']
                playUrl.value = parseUrl(data['flvStreamInfo'][0]['playUrl']);
                createFlvVideo(playUrl.value);
                log.rate = rate.value;
                log.url = playUrl.value;
            }).catch(e=>{
                log.url = e
            })
        }
        // 直播间信息组装
        const createLiveRoomInfo = (data)=>{
            liveInfo.uid = data['uid'];
            liveInfo.name = data['name'];
            liveInfo.avatar = data['avatar'];
            liveInfo.sex = data['sex'];
            liveInfo.roomId = data['roomId'];
            liveInfo.gameName = data['gameName'];
            liveInfo.startTime = data['startTime'];
            liveInfo.liveTitle = data['liveTitle'];
        }
        // 地址解析
        const parseUrl = (url)=>{
            let params = url.split("?")[1];
            params = params.split("&");
            let paramsObj = {};
            for (let i = 0; i < params.length; i++) {
                let item = params[i].split("=");
                2 === item.length && (paramsObj[item[0]] = item[1])
            }

            let mainUrl = url.split("?")[0];
            let r = mainUrl.split("/");
            let streamName = r[r.length - 1].replace(/.(flv|m3u8)/g, "");
            let {fm: fm, wsTime: wsTime, wsSecret: u, ...others} = paramsObj;
            let fmParse = Base64.decode(decodeURIComponent(fm));
            let p = fmParse.split("_")[0];
            let time = parseInt(1e4 * (new Date).getTime() + 1e4 * Math.random());
            let newWsSecret = `${p}_0_${streamName}_${time}_${wsTime}`;
            newWsSecret = md5.hex(newWsSecret);
            let y = "";
            Object.keys(others).forEach(e=>{
                y += `&${e}=${others[e]}`
            });
            return `${mainUrl}?wsSecret=${newWsSecret}&wsTime=${wsTime}&u=0&seqid=${time}${y}&ratio=${rate.value}`;
        }

        const changeRate = (para)=>{
            rate.value = para;
        }

        onMounted(()=>{
            //
        })
        onBeforeUnmount(()=>{
            destroyVideo();
        })

        return {log, searchContent, loadLiveInfo, rateConfig, flvStreamConfig, m3u8StreamConfig, liveInfo, rate, changeRate, isLive}
    }
  }).mount('#app')
</script>
</body>
</html>